/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.googlecode.eckoit.audio; import java.io.IOException; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.sound.sampled.DataLine; import javax.sound.sampled.TargetDataLine; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.Line; import javax.sound.sampled.Mixer; import javax.sound.sampled.Port; /** * * @author ryan */ public class SimpleAudioRecorder implements AudioRecorder { private static SimpleAudioRecorder singletonObject; private boolean isRecording = false; private long recordingStart; private TargetDataLine m_line; private AudioFileFormat.Type m_targetType; private AudioInputStream m_audioInputStream; private File root; private File m_outputFile; private SimpleAudioRecorder() { } public static synchronized SimpleAudioRecorder getSingletonObject() { if (singletonObject == null) { singletonObject = new SimpleAudioRecorder(); } return singletonObject; } @Override public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } @Override public synchronized void startRecording(String recordingId, String mixer, float gain) throws LineUnavailableException { if (isRecording || m_line != null || (m_line != null && m_line.isOpen())) { throw new LineUnavailableException(); } AudioFormat audioFormat = new AudioFormat(16000.0F, 16, 1, true, true); audioFormat = getBestAudioFormat(audioFormat, mixer); DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat); Mixer selectedMixer = getSelectedMixer(mixer); if (selectedMixer == null) { m_line = (TargetDataLine) AudioSystem.getLine(info); selectedMixer = AudioSystem.getMixer(null); } else { m_line = (TargetDataLine) selectedMixer.getLine(info); } m_line.open(audioFormat); //FloatControl fc = (FloatControl) selectedMixer.getControl(FloatControl.Type.MASTER_GAIN); //System.out.println("Master Gain min: " + fc.getMinimum()); //System.out.println("Master Gain min: " + fc.getMaximum()); //ystem.out.println("Master Gain cur: " + fc.getValue()); //fc.setValue(MIN_PRIORITY); AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE; m_audioInputStream = new AudioInputStream(m_line); m_targetType = targetType; m_outputFile = new File(root, recordingId + ".wav"); isRecording = true; recordingStart = System.currentTimeMillis(); new Recorder().start(); } /** Stops the recording. */ @Override public synchronized long stopRecording() { if (isRecording) { m_line.stop(); m_line.close(); m_line = null; isRecording = false; long now = System.currentTimeMillis(); return (now - recordingStart)/1000; } return -1; } @Override public boolean isRecording() { return isRecording; } public static Mixer getSelectedMixer(String selectedMixerIndex) { if (selectedMixerIndex.equals("default")) { return null; } else { Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo(); if (selectedMixerIndex.equals("last")) { return AudioSystem.getMixer(mixerInfo[mixerInfo.length - 1]); } else { int index = Integer.parseInt(selectedMixerIndex); return AudioSystem.getMixer(mixerInfo[index]); } } } public static AudioFormat getBestAudioFormat(AudioFormat desiredFormat, String selectedMixerIndex) { AudioFormat finalFormat = desiredFormat; DataLine.Info info = new DataLine.Info(TargetDataLine.class, desiredFormat); /* If we cannot get an audio line that matches the desired * characteristics, shoot for one that matches almost * everything we want, but has a higher sample rate. */ //if (!AudioSystem.isLineSupported(info)) { AudioFormat nativeFormat = getNativeAudioFormat(desiredFormat, getSelectedMixer(selectedMixerIndex)); if (nativeFormat == null) { //System.out.println("couldn't find suitable target audio format"); } else { finalFormat = nativeFormat; /* convert from native to the desired format if supported */ boolean doConversion = AudioSystem.isConversionSupported(desiredFormat, nativeFormat); if (doConversion) { //System.out.println("Converting from " + finalFormat.getSampleRate() // + "Hz to " + desiredFormat.getSampleRate() + "Hz"); } else { // System.out.println("Using native format: Cannot convert from " // + finalFormat.getSampleRate() + "Hz to " // + desiredFormat.getSampleRate() + "Hz"); } } //} else { // System.out.println("Desired format: " + desiredFormat + " supported."); // finalFormat = desiredFormat; //} return finalFormat; } public static AudioFormat getNativeAudioFormat(AudioFormat format, Mixer mixer) { Line.Info[] lineInfos; if (mixer != null) { lineInfos = mixer.getTargetLineInfo(new Line.Info(TargetDataLine.class)); } else { lineInfos = AudioSystem.getTargetLineInfo(new Line.Info(TargetDataLine.class)); } AudioFormat nativeFormat = null; // find a usable target line for (int i = 0; i < lineInfos.length; i++) { AudioFormat[] formats = ((TargetDataLine.Info) lineInfos[i]).getFormats(); for (int j = 0; j < formats.length; j++) { // for now, just accept downsampling, not checking frame // size/rate (encoding assumed to be PCM) AudioFormat thisFormat = formats[j]; if (thisFormat.getEncoding() == format.getEncoding() && thisFormat.isBigEndian() == format.isBigEndian() && thisFormat.getSampleSizeInBits() == format.getSampleSizeInBits() && thisFormat.getSampleRate() >= format.getSampleRate()) { nativeFormat = thisFormat; break; } } if (nativeFormat != null) { //no need to look through remaining lineinfos break; } } return nativeFormat; } public static final String dumpLineInfo(String selectedMixerIndex) { System.out.println("Selected Mixer: " + selectedMixerIndex); if ("".equals(selectedMixerIndex) || selectedMixerIndex == null) { return ""; } Mixer mixer = getSelectedMixer(selectedMixerIndex); if (mixer == null) { return "Default Audio device."; } Line.Info[] lineInfo = mixer.getTargetLineInfo(); StringBuilder info = new StringBuilder(""); if (lineInfo != null) { for (int i = 0; i < lineInfo.length; i++) { if (lineInfo[i] instanceof DataLine.Info) { AudioFormat[] formats = ((DataLine.Info) lineInfo[i]).getFormats(); for (int j = 0; j < formats.length; j++) { info.append("-" + formats[j] + "\n"); } } else if (lineInfo[i] instanceof Port.Info) { info.append("-" + lineInfo[i] + "\n"); } } } return info.toString(); } public static final List<String> dumpMixers() { /** Lists all the available audio devices. */ Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo(); List<String> results = new ArrayList<String>(); for (int i = 0; i < mixerInfo.length; i++) { Mixer mixer = AudioSystem.getMixer(mixerInfo[i]); StringBuilder builder = new StringBuilder(); builder.append("Mixer[" + i + "]: \"" + mixerInfo[i].getName() + "\""); builder.append(" " + mixerInfo[i].getDescription()); results.add(builder.toString()); } return results; } private class Recorder extends Thread { @Override public void run() { try { AudioSystem.write(m_audioInputStream, m_targetType, m_outputFile); } catch (IOException e) { e.printStackTrace(); } } /** Starts the recording. To accomplish this, (i) the line is started and (ii) the thread is started. */ @Override public void start() { m_line.start(); super.start(); } } }